
package br.ufrj.dcc.versionspaces.impl;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import br.ufrj.dcc.versionspaces.AbstractVersionSpaces;


/**
 *
 * @author Tiago Santos da Silva
 * @author Bruno Bottino Ferreira
 */


public class JavaVersionSpacesImpl implements AbstractVersionSpaces {

	//local variables
	private String[] attributes;
	private List<String>[] attributeDomain;
	private List<String> attributeColumnGoal;
	private String[] goalAttributeDomain;
	private Map<String, Integer> mapAttributes;
	private Map<String, Integer> mapAttributesGoal;
	private Vector<String>[] attributeColumnExample;
	private List<String[]> tree;
	private String goalAttribute;
	private int numberExamples;
	private int numberAttributes;

	public void declareAttributes(List<String> attributes) {
		this.attributes = new String[attributes.size()];
		mapAttributes = new HashMap<String, Integer>();
		int i = 0;

		attributeDomain = new Vector[attributes.size()];
		attributeColumnExample = new Vector[attributes.size()];

		for(String attribute : attributes)
		{
			this.attributes[i] = attribute;
			mapAttributes.put(attribute, i);
			i++;
		}

		numberAttributes = attributes.size();
	}

	public void declareGoalAttribute(String attribute) {
		goalAttribute = attribute;
	}

	public void declareAttributeDomain(String attribute, List<String> attributeDomain) {

		this.attributeDomain[mapAttributes.get(attribute)] = attributeDomain;
	}

	public void declareGoalAttributeDomain(List<String> attributeDomain) {

		goalAttributeDomain = new String[attributeDomain.size()];
		mapAttributesGoal = new HashMap<String, Integer>();
		int i = 0;

		for(String attribute : attributeDomain)
		{
			goalAttributeDomain[i] = attributeDomain.get(i);
			mapAttributesGoal.put(attribute, i);
			i++;
		}
	}

	public void setNumberOfTrainingEntries(int size) {

		numberExamples = size;

	}

	public void declareExamples(String attribute, List<String> attributeColumn) {
		int index = mapAttributes.get(attribute);
		attributeColumnExample[index] = new Vector();
		attributeColumnExample[index].addAll(attributeColumn);
	}

	public void declareGoals(List<String> attributeColumn) {

		attributeColumnGoal = attributeColumn;

	}

    /*
        Determines if a given hypothesis is consistent with an example or not.
        goal determines the value of the example's goal attribute.
    */
	private boolean consistent(String[] hip, String[] ex, boolean goal)
	{
		for(int i=0; i<numberAttributes; i++)
		{
			if(!(hip[i].equals("?") || hip[i].equals(ex[i])))
			{
				if(goal == false)
					return true;
				else
					return false;
			}
		}

		if(goal == true)
			return true;
		else
			return false;
	}

	/*
        Calculates the 'generality' of a hypothesis, measured by the number of '?' attributes
    */
	int generality(String[] hip)
	{
		int res = 0;

		for(int i=0; i<numberAttributes; i++)
		{
			if(hip[i].equals("?")) res++;
		}

		return res;
	}

	/*
        Determines if a given hypothesis is more general than another.
    */
	boolean isMoreGeneral(String[] hipg, String[] hips)
	{
		int i;

		//if hips is empty, everyone is more general than it, except another empty one
		if(hips[0].equals("0"))
		{
			if(hipg[0].equals("0"))
				return false;
			else
				return true;
		}

		//if an attribute is different, return false
		for(i=0; i<numberAttributes; i++)
		{
			if(!hipg[i].equals("?") && !hipg[i].equals(hips[i])) return false;
		}

		return generality(hipg) > generality(hips);
	}

	/*
        Determines if a given hypothesis is more specific than another.
    */
	boolean isMoreSpecific(String[] hips, String[] hipg)
	{
		int i;

		//if an attribute is different, return false
		for(i=0; i<numberAttributes; i++)
		{
			if(!hipg[i].equals("?") && !hipg[i].equals(hips[i])) return false;
		}

		return generality(hipg) > generality(hips);
	}

	public List<String[]>[] buildVersionSpace()
	{
		List<String[]> s = new Vector<String[]>(), g = new Vector<String[]>();

		String[] vazio = new String[numberAttributes];
		String[] inter = new String[numberAttributes];

		for(int i=0; i<numberAttributes; i++)
		{
			vazio[i] = "0";
			inter[i] = "?";
		}

        //start S with an empty hypothesis and G with an all '?' one
		s.add(vazio);
		g.add(inter);

		for(int i=0; i<numberExamples; i++)
		{
            //generate example
			String[] example = new String[numberAttributes];
			for(int j=0; j<numberAttributes; j++)
			{
				example[j] = attributeColumnExample[j].get(i);
			}

			if(attributeColumnGoal.get(i).equals("yes"))
			{
                //remove any elements of G inconsistent with the example
				for(int j=0; j<g.size(); j++)
				{
					if(!consistent(g.get(j), example, true))
					{
						g.remove(j);
						j--;
					}
				}

				int ssize = s.size();
				for(int h=0; h<ssize; h++)
				{
					String[] hip = s.get(h);
					s.remove(h);
					h--;
					ssize--;

                    //process each element in S inconsistent with the example
					if(!consistent(hip, example, true))
					{
						if(hip[0].equals("0"))
						{
                            //if empty, generalize to the example
							hip = example.clone();
							s.add(hip);
						}
						else
						{
							String[] newHip = hip.clone();

                            //generalize attributes that aren't consistent
							for(int j=0; j<numberAttributes; j++)
							{
								if(!hip[j].equals(example[j]))
								{
									newHip[j] = "?";
								}
							}

							//add the new hypothesys to S if it's more specific than some other in G
							for(String[] hipg : g)
							{
								if(isMoreGeneral(hipg, newHip))
								{
									s.add(newHip);
									break;
								}
							}

                            //remove from S any hypothesis that's more general than any other in S
							for(int j=0; j<s.size(); j++)
							{
								for(int k=j+1; k<s.size(); k++)
								{
									if(isMoreGeneral(s.get(k), s.get(j)))
									{
										s.remove(k);
										k--;
									}
								}
							}
						}
					}
				}
			}
			else
			{
                //remove any elements of S inconsistent with the example
				for(int j=0; j<s.size(); j++)
				{
					if(!consistent(s.get(j), example, false))
					{
						s.remove(j);
						j--;
					}
				}

				int gsize = g.size();
				for(int h=0; h<gsize; h++)
				{
					String[] hip = g.get(h);
					g.remove(h);
					h--;
					gsize--;

                    //process each element in G inconsistent with the example
					if(!consistent(hip, example, false))
					{
                        //for each '?' attribute, specify to all values different than the example
						for(int j=0; j<numberAttributes; j++)
						{
							if(hip[j].equals("?"))
							{
								for(int k=0; k<attributeDomain[j].size(); k++)
								{
									if(!attributeDomain[j].get(k).equals(example[j]))
									{
										String[] newHip = hip.clone();

										newHip[j] = attributeDomain[j].get(k);

                                        //add the new hypothesys to G if it's more general than some other in S
										for(String[] hips : s)
										{
											if(isMoreGeneral(newHip, hips))
											{
												g.add(newHip);
												break;
											}
										}
									}
								}
							}
						}

						//remove from G any hypothesis that's more specific than any other in
						for(int j=0; j<g.size(); j++)
						{
							for(int k=j+1; k<g.size(); k++)
							{
								if(isMoreSpecific(g.get(k), g.get(j)))
								{
									g.remove(k);
									k--;
								}
							}
						}
					}
				}
			}
		}

		List<String[]>[] ret = new Vector[2];
        ret[0] = g; ret[1] = s;
        return ret;
	}

public static void main(String[] args) {

		JavaVersionSpacesImpl dt = new JavaVersionSpacesImpl();

/*

		List<String> attributes = new Vector();

		attributes.add("outlook");
		attributes.add("temperature");
		attributes.add("humidity");
		attributes.add("wind");

		dt.declareAttributes(attributes);
		dt.setNumberOfTrainingEntries(14);

		dt.declareGoalAttribute("PlayTennis");

		List<String> attributeDomain = new Vector();

		attributeDomain.add("yes");
		attributeDomain.add("no");

		dt.declareGoalAttributeDomain(attributeDomain);

		// valores de outlook

		attributeDomain = new Vector();

		attributeDomain.add("sunny");
		attributeDomain.add("overcast");
		attributeDomain.add("rain");

		dt.declareAttributeDomain("outlook", attributeDomain);

		// valores de temperature

		attributeDomain = new Vector();

		attributeDomain.add("hot");
		attributeDomain.add("mild");
		attributeDomain.add("cool");

		dt.declareAttributeDomain("temperature", attributeDomain);

		// valores de humidity

		attributeDomain = new Vector();

		attributeDomain.add("high");
		attributeDomain.add("normal");

		dt.declareAttributeDomain("humidity", attributeDomain);

		// valores de wind

		attributeDomain = new Vector();

		attributeDomain.add("weak");
		attributeDomain.add("strong");

		dt.declareAttributeDomain("wind", attributeDomain);



		// exemplos outlook

		List atributo = new Vector();

		atributo.add("sunny");
		atributo.add("sunny");
		atributo.add("overcast");
		atributo.add("rain");
		atributo.add("rain");
		atributo.add("rain");
		atributo.add("overcast");
		atributo.add("sunny");
		atributo.add("sunny");
		atributo.add("rain");
		atributo.add("sunny");
		atributo.add("overcast");
		atributo.add("overcast");
		atributo.add("rain");

		dt.declareExamples("outlook", atributo);

		// exemplos temperature

		atributo = new Vector();

		atributo.add("hot");
		atributo.add("hot");
		atributo.add("hot");
		atributo.add("mild");
		atributo.add("cool");
		atributo.add("cool");
		atributo.add("cool");
		atributo.add("mild");
		atributo.add("cool");
		atributo.add("mild");
		atributo.add("mild");
		atributo.add("mild");
		atributo.add("hot");
		atributo.add("mild");

		dt.declareExamples("temperature", atributo);

		// Exemplos humidity

		atributo = new Vector();

		atributo.add("high");
		atributo.add("high");
		atributo.add("high");
		atributo.add("high");
		atributo.add("normal");
		atributo.add("normal");
		atributo.add("normal");
		atributo.add("high");
		atributo.add("normal");
		atributo.add("normal");
		atributo.add("normal");
		atributo.add("high");
		atributo.add("normal");
		atributo.add("high");

		dt.declareExamples("humidity", atributo);

		// Exemplos wind

		atributo = new Vector();

		atributo.add("weak");
		atributo.add("strong");
		atributo.add("weak");
		atributo.add("weak");
		atributo.add("weak");
		atributo.add("strong");
		atributo.add("strong");
		atributo.add("weak");
		atributo.add("weak");
		atributo.add("weak");
		atributo.add("strong");
		atributo.add("strong");
		atributo.add("weak");
		atributo.add("strong");

		dt.declareExamples("wind", atributo);

		// Exemplos playtennis

		atributo = new Vector();

		atributo.add("no");
		atributo.add("no");
		atributo.add("yes");
		atributo.add("yes");
		atributo.add("yes");
		atributo.add("no");
		atributo.add("yes");
		atributo.add("no");
		atributo.add("yes");
		atributo.add("yes");
		atributo.add("yes");
		atributo.add("yes");
		atributo.add("yes");
		atributo.add("no");

		dt.declareGoals(atributo);

		*/


		List<String> attributes = new Vector<String>();

		attributes.add("sky");
		attributes.add("temp");
		attributes.add("humid");
		attributes.add("wind");
		attributes.add("water");
		attributes.add("forecast");

		dt.declareAttributes(attributes);
		dt.setNumberOfTrainingEntries(4);

		dt.declareGoalAttribute("EnjoySpt");

		List<String> attributeDomain = new Vector<String>();

		attributeDomain.add("yes");
		attributeDomain.add("no");

		dt.declareGoalAttributeDomain(attributeDomain);

		// valores de sky
		attributeDomain = new Vector<String>();
		attributeDomain.add("sunny");
		attributeDomain.add("rainy");
		attributeDomain.add("cloudy");
		dt.declareAttributeDomain("sky", attributeDomain);
		// valores de temperature
		attributeDomain = new Vector<String>();
		attributeDomain.add("warm");
		attributeDomain.add("cold");
		dt.declareAttributeDomain("temp", attributeDomain);
		// valores de humidity
		attributeDomain = new Vector<String>();
		attributeDomain.add("normal");
		attributeDomain.add("high");
		dt.declareAttributeDomain("humid", attributeDomain);
		// valores de wind
		attributeDomain = new Vector<String>();
		attributeDomain.add("strong");
		attributeDomain.add("weak");
		dt.declareAttributeDomain("wind", attributeDomain);
		// valores de water
		attributeDomain = new Vector<String>();
		attributeDomain.add("warm");
		attributeDomain.add("cool");
		dt.declareAttributeDomain("water", attributeDomain);
		// valores de forecast
		attributeDomain = new Vector<String>();
		attributeDomain.add("same");
		attributeDomain.add("change");
		dt.declareAttributeDomain("forecast", attributeDomain);

		// exemplos sky
		List<String> atributo = new Vector<String>();
		atributo.add("sunny");
		atributo.add("sunny");
		atributo.add("rainy");
		atributo.add("sunny");
		dt.declareExamples("sky", atributo);
		// exemplos temp
		 atributo = new Vector<String>();
		atributo.add("warm");
		atributo.add("warm");
		atributo.add("cold");
		atributo.add("warm");
		dt.declareExamples("temp", atributo);
		// exemplos humid
		 atributo = new Vector<String>();
		atributo.add("normal");
		atributo.add("high");
		atributo.add("high");
		atributo.add("high");
		dt.declareExamples("humid", atributo);
		// exemplos wind
		 atributo = new Vector<String>();
		atributo.add("strong");
		atributo.add("strong");
		atributo.add("strong");
		atributo.add("strong");
		dt.declareExamples("wind", atributo);
		// exemplos water
		 atributo = new Vector<String>();
		atributo.add("warm");
		atributo.add("warm");
		atributo.add("warm");
		atributo.add("cool");
		dt.declareExamples("water", atributo);
		// exemplos forecast
		 atributo = new Vector<String>();
		atributo.add("same");
		atributo.add("same");
		atributo.add("change");
		atributo.add("change");
		dt.declareExamples("forecast", atributo);

		// Exemplos enjoySpt
		atributo = new Vector<String>();
		atributo.add("yes");
		atributo.add("yes");
		atributo.add("no");
		atributo.add("yes");
		dt.declareGoals(atributo);


		List<String[]>[] GeS = dt.buildVersionSpace();

		System.out.println("G Final:");
		for(String[] hip : GeS[0])
		{
			for(String s : hip) System.out.print(" " + s);
			System.out.println("");
		}
		System.out.println("S Final:");
		for(String[] hip : GeS[1])
		{
			for(String s : hip) System.out.print(" " + s);
			System.out.println("");
		}
		System.out.println("------");

		/*
		List<String[]> tree = dt.buildDecisionTree();

		for(String[] a : tree)
		{
			for(int i = 0; i <  a.length; i++)
			{
				System.out.print(a[i] + " ");
			}

			System.out.println();

		}*/

	}

}
